import pandas as pd
import numpy as np
import missingno
import matplotlib.pyplot as plt
import matplotlib
import seaborn as sns
%matplotlib inline
# 2021년 1월 자전거 대여이력 정보(서울시 공공데이터)를 사용한다.
rent = pd.read_csv('rent_1.csv')
rent.shape
(806503, 11)
rent.head(5)
| 자전거번호 | 대여일시 | 대여 대여소번호 | 대여 대여소명 | 대여거치대 | 반납일시 | 반납대여소번호 | 반납대여소명 | 반납거치대 | 이용시간 | 이용거리 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | SPB-53145 | 2021-01-02 20:50:36 | 3 | 중랑센터 | 0 | 2021-01-02 21:15:41 | 668 | 서울축산농협(장안지점) | 0 | 25 | 0.00 |
| 1 | SPB-53074 | 2021-01-04 16:02:12 | 3 | 중랑센터 | 0 | 2021-01-04 16:17:06 | 668 | 서울축산농협(장안지점) | 0 | 14 | 0.00 |
| 2 | SPB-50952 | 2021-01-13 21:02:05 | 3 | 중랑센터 | 0 | 2021-01-13 21:14:04 | 668 | 서울축산농협(장안지점) | 0 | 11 | 1853.99 |
| 3 | SPB-52380 | 2021-01-14 21:02:55 | 3 | 중랑센터 | 0 | 2021-01-14 21:13:41 | 668 | 서울축산농협(장안지점) | 0 | 10 | 0.00 |
| 4 | SPB-52380 | 2021-01-18 18:02:08 | 3 | 중랑센터 | 0 | 2021-01-18 18:15:22 | 540 | 군자역 7번출구 베스트샵 앞 | 0 | 13 | 2291.05 |
# 결측치 확인 결과 모든 column에서 결측치가 발견되지 않았다.
rent.isnull().sum()
자전거번호 0 대여일시 0 대여 대여소번호 0 대여 대여소명 0 대여거치대 0 반납일시 0 반납대여소번호 0 반납대여소명 0 반납거치대 0 이용시간 0 이용거리 0 dtype: int64
# 한글폰트 없이 사용 시 matplotlib에서 오류가 발생하게 된다.
# 한글폰트를 미리 적용시켜준다.
# 이 때, 미리 폰트를 설치하고 가상환경의 matplotlib의 폰트 경로에 폰트를 복사해주고,
# 커널을 재시작해야 한다.
# 아래 코드로 제대로 설치되었는지 확인할 수 있다.
# 다음 블로그 글을 참고할 것: https://dsstudy.tistory.com/15
# import matplotlib
# import matplotlib.font_manager
# [f.name for f in matplotlib.font_manager.fontManager.ttflist if 'Nanum' in f.name]
plt.rcParams['font.family'] = 'NanumGothic'
# 결측치 시각화를 위한 라이브러리 missingno를 사용한다.
missingno.matrix(rent, figsize=(12,6))
plt.show()
# 수치형 자료 중 의미있는 column만을 선택하여 통계량 확인
# 1회 대여시의 평균적인 이용거리, 이용시간을 확인할 수 있다.
# 평균 2780m를 이동하며, 이용시간은 25분 가량이다.
rent[['이용거리', '이용시간']].describe()
| 이용거리 | 이용시간 | |
|---|---|---|
| count | 806503.000000 | 806503.00000 |
| mean | 2783.126334 | 25.69352 |
| std | 3558.647413 | 28.53219 |
| min | 0.000000 | 1.00000 |
| 25% | 759.320000 | 7.00000 |
| 50% | 1596.080000 | 15.00000 |
| 75% | 3355.420000 | 34.00000 |
| max | 93737.660000 | 1589.00000 |
# 객체타입의 데이터를 다루고 싶을 경우, include object 설정을 추가한다.
# unique한 데이터의 수, 가장 많이 출현한 데이터와 그 출현횟수에 대한 정보가 추가된다.
# 2021년 1월 가장 많이 대여된 대여소는 여의나루역이며, 뚝섬유원지역에서 반납된 경우가 가장 많다.
# 80여만건 중 한 개의 대여소에서 최대 2600여건이 각각 대여되고, 최대 3100여건이 반납되었다.
rent.describe(include=object)
| 자전거번호 | 대여일시 | 대여 대여소명 | 반납일시 | 반납대여소명 | |
|---|---|---|---|---|---|
| count | 806503 | 806503 | 806503 | 806503 | 806503 |
| unique | 18945 | 623499 | 2186 | 623142 | 2183 |
| top | SPB-53496 | 2021-01-25 18:08:18 | 여의나루역 1번출구 앞 | 2021-01-23 18:07:29 | 뚝섬유원지역 1번출구 앞 |
| freq | 166 | 9 | 2592 | 8 | 3091 |
# 대여/반납 대여소의 수
print(rent['대여 대여소명'].nunique())
print(rent['반납대여소명'].nunique())
2186 2183
print('대여량 많은 대여소 TOP 5 \n\n', rent['대여 대여소명'].value_counts().head(5), end='\n\n\n')
print('반납량 많은 대여소 TOP5 \n\n', rent['반납대여소명'].value_counts().head(5))
대여량 많은 대여소 TOP 5 여의나루역 1번출구 앞 2592 뚝섬유원지역 1번출구 앞 2554 봉림교 교통섬 2345 롯데월드타워(잠실역2번출구 쪽) 2076 마곡나루역 2번 출구 2016 Name: 대여 대여소명, dtype: int64 반납량 많은 대여소 TOP5 뚝섬유원지역 1번출구 앞 3091 여의나루역 1번출구 앞 2676 봉림교 교통섬 2405 마포구민체육센터 앞 2260 롯데월드타워(잠실역2번출구 쪽) 2084 Name: 반납대여소명, dtype: int64
# 데이터 타입을 변경하여 시간 정보를 더욱 적극적으로 활용할 수 있도록 만든다.
rent['대여일시'] = pd.to_datetime(rent['대여일시'])
rent['반납일시'] = pd.to_datetime(rent['반납일시'])
rent.dtypes
자전거번호 object 대여일시 datetime64[ns] 대여 대여소번호 int64 대여 대여소명 object 대여거치대 int64 반납일시 datetime64[ns] 반납대여소번호 int64 반납대여소명 object 반납거치대 int64 이용시간 int64 이용거리 float64 dtype: object
# 1월에 한정된 자료이므로, 일별 대여 추이를 확인하기 위해 day 단위로 분리해본다.
rent['대여일'] = rent['대여일시'].dt.day
rent['반납일'] = rent['반납일시'].dt.day
rent['대여일'].head(5)
0 2 1 4 2 13 3 14 4 18 Name: 대여일, dtype: int64
# 1월의 일별 자전거 대여량 변화 추이
# 각 대여일 별로 수를 count하고, index를 따로 지정해주기 위해 reset한다.
# 대여와 반납을 구분하여 보기 위해서, 구분 column을 새롭게 만들어 값을 부여한다.
# 전체 데이터를 day에 따라 정렬하고, 두 구분/반납 데이터프레임을 합친다.
rent_by_date = rent['대여일'].value_counts().reset_index()
rent_by_date.columns = ['day', 'count']
rent_by_date = rent_by_date.sort_values('day')
rent_by_date['구분'] = '대여'
return_by_date = rent['반납일'].value_counts().reset_index()
return_by_date.columns = ['day', 'count']
return_by_date = return_by_date.sort_values('day')
return_by_date['구분'] = '반납'
bike_by_date = pd.concat([rent_by_date, return_by_date])
bike_by_date
| day | count | 구분 | |
|---|---|---|---|
| 18 | 1 | 22119 | 대여 |
| 15 | 2 | 23463 | 대여 |
| 20 | 3 | 21656 | 대여 |
| 7 | 4 | 32732 | 대여 |
| 10 | 5 | 28819 | 대여 |
| ... | ... | ... | ... |
| 2 | 27 | 45355 | 반납 |
| 16 | 28 | 22813 | 반납 |
| 14 | 29 | 24150 | 반납 |
| 11 | 30 | 28075 | 반납 |
| 4 | 31 | 38297 | 반납 |
62 rows × 3 columns
# 일평균 26016회의 대여 및 반납이 이루어졌다.
bike_by_date[bike_by_date['구분'] == '대여']['count'].mean()
bike_by_date[bike_by_date['구분'] == '반납']['count'].mean()
26016.225806451614
# 위 테이블을 시각화한다
plt.figure(figsize=(12, 8))
sns.barplot(data=bike_by_date, x='day', y='count', hue='구분')
plt.title('2021/01 일별 자전거 대여수 추이')
plt.show()
# 요일에 대한 데이터는 당장 존재하기 않기 때문에, 이를 구분할 수 있는 새로운 데이터를 생성한다.
rent['대여요일'] = rent['대여일시'].dt.dayofweek
rent['반납요일'] = rent['반납일시'].dt.dayofweek
# 일별 데이터 측정 시에 진행한 것과 같이, 테이블의 구성을 변경하고 대여반납 구분을 추가한다.
rent_by_wd = rent['대여요일'].value_counts().reset_index()
rent_by_wd.columns = ['day_of_week', 'count']
rent_by_wd = rent_by_wd.sort_values('day_of_week')
rent_by_wd['구분'] = '대여'
return_by_wd = rent['반납요일'].value_counts().reset_index()
return_by_wd.columns = ['day_of_week', 'count']
return_by_wd = return_by_wd.sort_values('day_of_week')
return_by_wd['구분'] = '반납'
bike_by_wd = pd.concat([rent_by_wd, return_by_wd])
bike_by_wd
| day_of_week | count | 구분 | |
|---|---|---|---|
| 2 | 0 | 124405 | 대여 |
| 5 | 1 | 94330 | 대여 |
| 3 | 2 | 123854 | 대여 |
| 6 | 3 | 77773 | 대여 |
| 4 | 4 | 121210 | 대여 |
| 1 | 5 | 126175 | 대여 |
| 0 | 6 | 138756 | 대여 |
| 2 | 0 | 124562 | 반납 |
| 5 | 1 | 94583 | 반납 |
| 3 | 2 | 123672 | 반납 |
| 6 | 3 | 78103 | 반납 |
| 4 | 4 | 120676 | 반납 |
| 1 | 5 | 126044 | 반납 |
| 0 | 6 | 138863 | 반납 |
# 요일을 문자열로 구분시킨다
wd_map = {0: '월', 1:'화', 2:'수',3:'목',4:'금',5:'토',6:'일'}
bike_by_wd['weekday'] = bike_by_wd['day_of_week'].apply(lambda x: wd_map[x])
# 시각화
plt.figure(figsize=(12, 8))
sns.barplot(data=bike_by_wd, x='weekday', y='count', hue='구분')
plt.title('2021/01 요일별 대여반납 추이')
plt.show()
# 앞서 진행한 과정을 동일하게 유지하도록 한다.
rent['대여시'] = rent['대여일시'].dt.hour
rent['반납시'] = rent['반납일시'].dt.hour
rent_by_hour = rent['대여시'].value_counts().reset_index()
rent_by_hour.columns = ['hour', 'count']
rent_by_hour = rent_by_hour.sort_values('hour')
rent_by_hour['구분'] = '대여'
return_by_hour = rent['반납시'].value_counts().reset_index()
return_by_hour.columns = ['hour', 'count']
return_by_hour = return_by_hour.sort_values('hour')
return_by_hour['구분'] = '반납'
bike_by_hour = pd.concat([rent_by_hour, return_by_hour])
plt.figure(figsize=(12,8))
sns.barplot(data=bike_by_hour, x="hour", y="count", hue="구분")
plt.title('2021/01 시간별 대여/반납 추이 변화')
plt.show()
stations = pd.read_csv('bike_stations.csv')
stations.head(5)
| 대여소번호 | 보관소(대여소)명 | 자치구 | 상세주소 | 위도 | 경도 | 설치시기 | LCD대수 | QR대수 | 운영방식 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 301 | 경복궁역 7번출구 앞 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575794 | 126.971451 | 2015-10-07 | 16.0 | NaN | LCD |
| 1 | 302 | 경복궁역 4번출구 뒤 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575947 | 126.974060 | 2015-10-07 | 12.0 | NaN | LCD |
| 2 | 303 | 광화문역 1번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하189 | 37.571770 | 126.974663 | 2015-10-07 | 8.0 | NaN | LCD |
| 3 | 304 | 광화문역 2번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하172 | 37.572113 | 126.977577 | 2021-01-26 | NaN | 7.0 | QR |
| 4 | 305 | 종로구청 옆 | 종로구 | 서울특별시 종로구 삼봉로 43 | 37.572582 | 126.978355 | 2015-10-07 | 16.0 | NaN | LCD |
# 거치대수 결측치를 0으로 만들고 이를 합쳐준다
stations = stations.fillna(0)
stations['거치대수'] = stations['LCD대수'] + stations['QR대수']
stations = stations.astype({'거치대수': 'int'})
stations.head(5)
| 대여소번호 | 보관소(대여소)명 | 자치구 | 상세주소 | 위도 | 경도 | 설치시기 | LCD대수 | QR대수 | 운영방식 | 거치대수 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 301 | 경복궁역 7번출구 앞 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575794 | 126.971451 | 2015-10-07 | 16.0 | 0.0 | LCD | 16 |
| 1 | 302 | 경복궁역 4번출구 뒤 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575947 | 126.974060 | 2015-10-07 | 12.0 | 0.0 | LCD | 12 |
| 2 | 303 | 광화문역 1번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하189 | 37.571770 | 126.974663 | 2015-10-07 | 8.0 | 0.0 | LCD | 8 |
| 3 | 304 | 광화문역 2번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하172 | 37.572113 | 126.977577 | 2021-01-26 | 0.0 | 7.0 | QR | 7 |
| 4 | 305 | 종로구청 옆 | 종로구 | 서울특별시 종로구 삼봉로 43 | 37.572582 | 126.978355 | 2015-10-07 | 16.0 | 0.0 | LCD | 16 |
# 구별 거치대 수 분류 및 카운트
# 주로 강 근처 지역들이 거치대가 많이 설치되어 있다
stations_by_ward = stations.groupby(['자치구'])['거치대수'].sum()
stations_by_ward = stations_by_ward.reset_index()
stations_by_ward.columns = ['자치구', '총 거치대 수']
stations_by_ward = stations_by_ward.sort_values(by='총 거치대 수', ascending=False)
stations_by_ward
| 자치구 | 총 거치대 수 | |
|---|---|---|
| 17 | 송파구 | 1720 |
| 3 | 강서구 | 1677 |
| 14 | 서초구 | 1591 |
| 0 | 강남구 | 1482 |
| 19 | 영등포구 | 1475 |
| 12 | 마포구 | 1283 |
| 1 | 강동구 | 1152 |
| 6 | 구로구 | 1136 |
| 8 | 노원구 | 1131 |
| 22 | 종로구 | 1125 |
| 18 | 양천구 | 1018 |
| 13 | 서대문구 | 1015 |
| 5 | 광진구 | 1004 |
| 15 | 성동구 | 941 |
| 16 | 성북구 | 901 |
| 21 | 은평구 | 897 |
| 10 | 동대문구 | 872 |
| 23 | 중구 | 855 |
| 11 | 동작구 | 844 |
| 24 | 중랑구 | 830 |
| 4 | 관악구 | 819 |
| 20 | 용산구 | 797 |
| 7 | 금천구 | 717 |
| 9 | 도봉구 | 648 |
| 2 | 강북구 | 578 |
# 구별 거치대 수 시각화
plt.figure(figsize=(12, 8))
sns.barplot(data=stations_by_ward, x='자치구', y='총 거치대 수')
plt.title('서울시 공공자전거 대여소 구별 거치대수(2021/01/31)')
plt.show()
# 이제 사용하지 않을 설치 시기 및 운영 방식/거치대 수 column은 제거해준다.
stations = stations.drop(['설치시기', 'LCD대수', 'QR대수', '운영방식'], axis='columns')
stations.head(5)
| 대여소번호 | 보관소(대여소)명 | 자치구 | 상세주소 | 위도 | 경도 | |
|---|---|---|---|---|---|---|
| 0 | 301 | 경복궁역 7번출구 앞 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575794 | 126.971451 |
| 1 | 302 | 경복궁역 4번출구 뒤 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575947 | 126.974060 |
| 2 | 303 | 광화문역 1번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하189 | 37.571770 | 126.974663 |
| 3 | 304 | 광화문역 2번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하172 | 37.572113 | 126.977577 |
| 4 | 305 | 종로구청 옆 | 종로구 | 서울특별시 종로구 삼봉로 43 | 37.572582 | 126.978355 |
# 위에서 결측치를 0으로 대체했기 때문에, 위도/경도에 0값으로 채워진 행이 존재한다
# 위도, 경도값이 0에 해당하는 행을 삭제해준다
# 제거 결과 약 100여개 행이 사라졌다.
stations = stations[stations['위도'] != 0]
stations.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 2042 entries, 0 to 2153 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 대여소번호 2042 non-null int64 1 보관소(대여소)명 2042 non-null object 2 자치구 2042 non-null object 3 상세주소 2042 non-null object 4 위도 2042 non-null float64 5 경도 2042 non-null float64 6 설치시기 2042 non-null object 7 LCD대수 2042 non-null float64 8 QR대수 2042 non-null float64 9 운영방식 2042 non-null object 10 거치대수 2042 non-null int64 dtypes: float64(4), int64(2), object(5) memory usage: 191.4+ KB
# 전체 대여소의 위치를 지도로 시각화
# 위 분석에서 강 근처의 지역에서 거치대가 많았으나,
# 실제 대여소의 분포로는 생각보다 균일하게 분산되어있는 것으로 보인다
# 이는 강 근처의 대여소들이 더 많은 거치대가 설치되어 있는 것이리라 생각된다.
import folium
map = folium.Map(location=[stations['위도'].mean(), stations['경도'].mean()], zoom_start=12)
for n in stations.index:
folium.Circle(
location=[stations['위도'][n], stations['경도'][n]],
radius=20,
color='green',
fill=True,
fill_color='green'
).add_to(map)
map
# 대여소별로 대여 및 반납 횟수를 파악하고 싶다
# 대여소번호를 기준으로 위의 이용내역 데이터와 대여소 데이터를 병합할 것이다.
rent_counts = rent['대여 대여소번호'].value_counts()
return_counts = rent['반납대여소번호'].value_counts()
rent_counts = rent_counts.reset_index()
return_counts = return_counts.reset_index()
rent_counts.columns = ['대여소번호', '대여횟수']
return_counts.columns = ['대여소번호', '반납횟수']
<class 'pandas.core.frame.DataFrame'> RangeIndex: 2185 entries, 0 to 2184 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 대여소번호 2185 non-null int64 1 반납횟수 2185 non-null int64 dtypes: int64(2) memory usage: 34.3 KB <class 'pandas.core.frame.DataFrame'> RangeIndex: 2188 entries, 0 to 2187 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 대여소번호 2188 non-null int64 1 대여횟수 2188 non-null int64 dtypes: int64(2) memory usage: 34.3 KB
# 우선 대여소번호 당 대여횟수, 반납횟수를 하나의 테이블로 만들어준다
stations_counts = pd.merge(rent_counts, return_counts, on='대여소번호')
stations_counts
| 대여소번호 | 대여횟수 | 반납횟수 | |
|---|---|---|---|
| 0 | 207 | 2592 | 2676 |
| 1 | 502 | 2554 | 3091 |
| 2 | 2102 | 2345 | 2405 |
| 3 | 1210 | 2076 | 2084 |
| 4 | 2715 | 2016 | 1989 |
| ... | ... | ... | ... |
| 2180 | 1007 | 8 | 7 |
| 2181 | 3527 | 7 | 5 |
| 2182 | 2536 | 5 | 9 |
| 2183 | 2539 | 2 | 2 |
| 2184 | 2918 | 1 | 11 |
2185 rows × 3 columns
# 위에서 만든 테이블과 대여소 자료를 병합해준다.
stations = pd.merge(stations, stations_counts, on='대여소번호')
stations
| 대여소번호 | 보관소(대여소)명 | 자치구 | 상세주소 | 위도 | 경도 | 설치시기 | LCD대수 | QR대수 | 운영방식 | 거치대수 | 대여횟수 | 반납횟수 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 301 | 경복궁역 7번출구 앞 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575794 | 126.971451 | 2015-10-07 | 16.0 | 0.0 | LCD | 16 | 413 | 333 |
| 1 | 302 | 경복궁역 4번출구 뒤 | 종로구 | 서울특별시 종로구 사직로 지하130 | 37.575947 | 126.974060 | 2015-10-07 | 12.0 | 0.0 | LCD | 12 | 711 | 721 |
| 2 | 303 | 광화문역 1번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하189 | 37.571770 | 126.974663 | 2015-10-07 | 8.0 | 0.0 | LCD | 8 | 587 | 590 |
| 3 | 304 | 광화문역 2번출구 앞 | 종로구 | 서울특별시 종로구 세종대로 지하172 | 37.572113 | 126.977577 | 2021-01-26 | 0.0 | 7.0 | QR | 7 | 391 | 405 |
| 4 | 305 | 종로구청 옆 | 종로구 | 서울특별시 종로구 삼봉로 43 | 37.572582 | 126.978355 | 2015-10-07 | 16.0 | 0.0 | LCD | 16 | 508 | 542 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2025 | 1085 | 래미안 솔베뉴(고덕로) | 강동구 | 양재대로1650 | 37.554569 | 127.145874 | 2020-01-13 | 0.0 | 20.0 | QR | 20 | 173 | 132 |
| 2026 | 1086 | 래미안 솔베뉴(구천면로) | 강동구 | 양재대로1650 | 37.550598 | 127.145454 | 2020-01-10 | 0.0 | 10.0 | QR | 10 | 141 | 140 |
| 2027 | 1088 | 풍납사거리(강동) | 강동구 | 성내동451-3 | 37.528122 | 127.119522 | 2020-01-14 | 0.0 | 12.0 | QR | 12 | 212 | 218 |
| 2028 | 1089 | 고덕 래미안힐스테이트(201동) | 강동구 | 고덕동 675 | 37.560600 | 127.146698 | 2020-01-13 | 0.0 | 10.0 | QR | 10 | 52 | 53 |
| 2029 | 1090 | 상일동역 2번출구 앞 | 강동구 | 고덕로 333 | 37.556789 | 127.166145 | 2020-01-13 | 0.0 | 10.0 | QR | 10 | 627 | 639 |
2030 rows × 13 columns
# 자치구를 기준으로 대여 및 반납 횟수를 확인해본다
# 기본적으로 대여가 많이 발생하는 곳에서 반납도 많이 발생한다.
# 이는 결국 한 번의 대여 시 이동하는 반경이 자치구의 범위를 크게 벗어나지 않는다는 의미이다.
rent_by_ward = stations.groupby(['자치구'])[['대여횟수', '반납횟수']].sum()
rent_by_ward = rent_by_ward.reset_index()
rent_by_ward.columns = ['자치구', '총 대여 횟수', '총 반납 횟수']
rent_by_ward = rent_by_ward.sort_values(by='총 대여 횟수', ascending=False)
rent_by_ward
| 자치구 | 총 대여 횟수 | 총 반납 횟수 | |
|---|---|---|---|
| 3 | 강서구 | 67832 | 68060 |
| 19 | 영등포구 | 63714 | 63606 |
| 17 | 송파구 | 52806 | 53207 |
| 12 | 마포구 | 47344 | 49052 |
| 18 | 양천구 | 42935 | 43301 |
| 8 | 노원구 | 41774 | 42023 |
| 5 | 광진구 | 37294 | 37445 |
| 15 | 성동구 | 34222 | 34919 |
| 22 | 종로구 | 33798 | 32993 |
| 6 | 구로구 | 30260 | 30328 |
| 10 | 동대문구 | 29711 | 30115 |
| 14 | 서초구 | 28698 | 28683 |
| 4 | 관악구 | 27210 | 26519 |
| 1 | 강동구 | 25846 | 26223 |
| 16 | 성북구 | 25359 | 24871 |
| 21 | 은평구 | 24713 | 24985 |
| 0 | 강남구 | 24434 | 23891 |
| 13 | 서대문구 | 22866 | 21474 |
| 24 | 중랑구 | 22645 | 22685 |
| 11 | 동작구 | 18785 | 19189 |
| 23 | 중구 | 18766 | 18035 |
| 20 | 용산구 | 16654 | 16880 |
| 9 | 도봉구 | 15665 | 15552 |
| 2 | 강북구 | 15149 | 15098 |
| 7 | 금천구 | 13414 | 13358 |
# 구별 총 대여/반납 횟수 시각화
fig = plt.figure(figsize=(12, 8))
area1 = fig.add_subplot(1, 2, 1)
area1.set_title('서울시 공공자전거 대여소 구별 대여횟수(2021/01)')
area2 = fig.add_subplot(1, 2, 2)
area2.set_title('서울시 공공자전거 대여소 구별 반납횟수(2021/01)')
sns.barplot(data=rent_by_ward, x='자치구', y='총 대여 횟수', ax=area1)
sns.barplot(data=rent_by_ward, x='자치구', y='총 반납 횟수', ax=area2)
plt.show()
rent_by_ward = rent_by_ward.set_index('자치구')
rent_by_ward['이용건수'] = rent_by_ward['총 대여 횟수'] + rent_by_ward['총 반납 횟수']
rent_by_ward
# 어떤 자치구가 가장 활발하게 대여와 반납이 이루어지는가?
import json
map = folium.Map(
location=[stations['위도'].mean(), stations['경도'].mean()],
zoom_start=11,
tiles='stamentoner'
)
# geo json은 외부에서 가져온 서울시 자치구 지도 json데이터이다.
geo_str = json.load(open('geo.json', encoding='utf-8'))
map.choropleth(
geo_data=geo_str,
data=rent_by_ward['이용건수'],
columns=[rent_by_ward.index, rent_by_ward['이용건수']],
fill_color='PuRd',
key_on='feature.id',
legend_name='이용건수'
)
map
/home/hanoul/.pyenv/versions/3.8.2/envs/week5_eda/lib/python3.8/site-packages/folium/folium.py:409: FutureWarning: The choropleth method has been deprecated. Instead use the new Choropleth class, which has the same arguments. See the example notebook 'GeoJSON_and_choropleth' for how to do this. warnings.warn(
# 이번엔 각 자치구 내에서도 어떤 대여소들이 가장 많이 사용되는지
# 지도로 시각화해보고자 한다
# 각 대여소별로 대여 빈도수를 히트맵으로 보여줄 것이다.
rent_num = rent[['대여 대여소번호']]
rent_num.columns = ['대여소번호']
new_rent = pd.merge(rent_num, stations, on='대여소번호')
rent_by_stations = new_rent[['대여소번호', '위도', '경도']]
rent_by_stations
| 대여소번호 | 위도 | 경도 | |
|---|---|---|---|
| 0 | 101 | 37.549561 | 126.905754 |
| 1 | 101 | 37.549561 | 126.905754 |
| 2 | 101 | 37.549561 | 126.905754 |
| 3 | 101 | 37.549561 | 126.905754 |
| 4 | 101 | 37.549561 | 126.905754 |
| ... | ... | ... | ... |
| 781889 | 4202 | 37.546040 | 126.926727 |
| 781890 | 4202 | 37.546040 | 126.926727 |
| 781891 | 4202 | 37.546040 | 126.926727 |
| 781892 | 4202 | 37.546040 | 126.926727 |
| 781893 | 4202 | 37.546040 | 126.926727 |
781894 rows × 3 columns
# 대여소별 대여 히트맵의 분포
from folium.plugins import HeatMap
map = folium.Map(
location=[stations['위도'].mean(), stations['경도'].mean()],
zoom_start=11,
tiles='stamentoner'
)
heatMap = HeatMap(
zip(rent_by_stations['위도'], rent_by_stations['경도']),
min_opacity=0.2,
radius=15, blur=10,
max_zoom=5,
color='red'
)
map.add_child(heatMap)